﻿using System;
using System.Collections.Generic;
using Internal.Runtime.Models;
using Runtime.Models;
using Runtime.Services.Battle.Entity;
using Runtime.Services.BattleSkill;
using UnityEngine;
using UnityEngine.Pool;

namespace Runtime.Services.Battle.Behaviour {

    /// <summary>
    /// 移动行为
    /// </summary>
    public interface IDFR_BattleBehaviour_Movement : IDFR_BattleBehaviour {

        public static readonly DFR_BattleBehaviourType TYPE = new() { Value = int.MaxValue - 2 };

        public class WayPoint {
            public DFR_BattleDirection Direction { get; private set; }
            public Vector3 From { get; private set; }
            public Vector3 To { get; private set; }
            public float FromWayPosition { get; private set; }
            public float ToWayPosition { get; private set; }
            public float FromInterpolation { get; private set; }
            public float ToInterpolation { get; private set; }
            public void Initialize(DFR_BattleDirection direction, Vector3 @from, Vector3 to, float fromWayPosition, float toWayPosition, float fromInterpolation, float toInterpolation) {
                //UnityEngine.Debug.Log($"{nameof(direction)}:{direction}, {nameof(@from)}:{@from} {nameof(to)}:{to}, {nameof(fromWayPosition)}: {fromWayPosition}, {nameof(toWayPosition)}:{toWayPosition}, {nameof(fromInterpolation)}:{fromInterpolation}, {nameof(toInterpolation)}:{toInterpolation}");
                Direction = direction;
                From = from;
                To = to;
                FromWayPosition = fromWayPosition;
                ToWayPosition = toWayPosition;
                FromInterpolation = fromInterpolation;
                ToInterpolation = toInterpolation;
            }
        }

        /// <summary>
        /// 实体
        /// </summary>
        new public interface IEntity : IDFR_BattleBehaviour.IEntity {

            /// <summary>
            /// 逻辑路径位置
            /// </summary>
            F64 LogicWayPosition { get; set; }

            /// <summary>
            /// 显示路径位置
            /// </summary>
            float WayPosition { get; set; }
        }

        public sealed class Info : DFR_BattleBehaviourInfo, IDisposable {

            private static readonly ObjectPool<Queue<WayPoint>> m_WayPointQueuePool = new(() => new(), actionOnRelease: item => item.Clear(), collectionCheck: false);

            public Queue<WayPoint> WayPointQueue { get; private set; }

            public Info() {
                WayPointQueue = m_WayPointQueuePool.Get();
            }

            void IDisposable.Dispose() {
                m_WayPointQueuePool.Release(WayPointQueue);
                WayPointQueue = null;
            }
        }

        /// <summary>
        /// 初始化
        /// </summary>
        /// <param name="model">数据</param>
        void Initialize(IDFR_BattleMovementModel model, bool isReverse);
    }

    [Implement(typeof(IDFR_BattleBehaviour_Movement))]
    internal sealed class DFR_BattleBehaviour_Movement : DFR_BattleBehaviour<IDFR_BattleBehaviour_Movement.Info>, IDFR_BattleBehaviour_Movement {

        private static readonly ObjectPool<IDFR_BattleBehaviour_Movement.WayPoint> m_WayPointPool = new(() => new(), collectionCheck: false);

        new private IDFR_BattleBehaviour_Movement.IEntity Entity => (IDFR_BattleBehaviour_Movement.IEntity)base.Entity;

        private IDFR_BattleMovementModelInternl m_Model;
        private bool m_IsReverse;

        private F64 m_LastLogicWayPosition;

        private bool m_IsFirstFrame = true;

        private HashSet<DFR_BattleEntityInstanceID> m_EnterHaloInstanceIDSet;
        private HashSet<DFR_BattleEntityInstanceID> m_ExistHaloInstanceIDSet;

        public void Initialize(IDFR_BattleMovementModel model, bool IsReverse) {
            m_Model = (IDFR_BattleMovementModelInternl)model;
            m_IsReverse = IsReverse;

            m_EnterHaloInstanceIDSet = HashSetPool<DFR_BattleEntityInstanceID>.Get();
            m_ExistHaloInstanceIDSet = HashSetPool<DFR_BattleEntityInstanceID>.Get();
        }

        protected override void Initialize() {
        }

        protected override void Allocate() {
            base.Allocate();
            m_IsFirstFrame = true;
        }

        protected override void Release() {
            m_Model = null;
            m_EnterHaloInstanceIDSet = null;
            m_ExistHaloInstanceIDSet = null;
            m_LastLogicWayPosition = F64.Zero;
            m_IsReverse = false;
            base.Release();
        }

        protected override IDFR_BattleBehaviour_Movement.Info LogicFrame() {
            m_Model.SetLastPosition(m_Model.WayPosition.Value, m_Model.MapPosition);
            var moveSpeedDelta = Delta(m_Model.MoveSpeed.Value);
            if (moveSpeedDelta > F64.Zero) {
                Entity.LogicWayPosition += moveSpeedDelta;
            }

            var way = BattleServiceTD.Instance.GetWay(m_Model.WayID);
            var lastWayPosition = F64.Max(m_Model.LastWayPosition + F64.Half, F64.Zero);
            var currentWayPosition = F64.Max(m_Model.WayPosition.Value + F64.Half, F64.Zero);
            var lastPoint = way.Get(way.Count - (lastWayPosition.Int + 1), m_IsReverse);
            var isEnd = false;
            if (currentWayPosition.Int >= way.Length) {
                isEnd = true;
            }
            var currentPoint = isEnd ? way.Get(0, m_IsReverse) : way.Get(way.Count - (currentWayPosition.Int + 1), m_IsReverse);
            if (m_IsFirstFrame || lastPoint != currentPoint) {
                if ((DFR_BattleTD.DataCenter.Models.CellDictionary.TryGetValue(lastPoint.Axis.ToIndex(), out var lastCellModel) && !m_IsFirstFrame) || isEnd) {
                    lastCellModel.AsSetter().Exit(m_Model.EntityInstanceID);
                }
                if (DFR_BattleTD.DataCenter.Models.CellDictionary.TryGetValue(currentPoint.Axis.ToIndex(), out var currentCellModel) || m_IsFirstFrame) {
                    currentCellModel.AsSetter().Enter(m_Model.EntityInstanceID);
                }
            }

            BattleSkillServiceTD.Instance.TestHalo(m_Model.EntityInstanceID, m_EnterHaloInstanceIDSet);
            foreach (var existHaloInstanceID in m_ExistHaloInstanceIDSet) {
                if (!m_EnterHaloInstanceIDSet.Contains(existHaloInstanceID)) {
                    BattleSkillServiceTD.Instance.RemoveBuff(m_Model.EntityInstanceID, existHaloInstanceID);
                }
            }
            m_ExistHaloInstanceIDSet.Clear();
            foreach (var enterHaloInstanceID in m_EnterHaloInstanceIDSet) {
                m_ExistHaloInstanceIDSet.Add(enterHaloInstanceID);
            }
            m_EnterHaloInstanceIDSet.Clear();
            BattleSkillServiceTD.Instance.RefreshBuff(m_Model.EntityInstanceID, m_ExistHaloInstanceIDSet);
            m_IsFirstFrame = false;

            return new IDFR_BattleBehaviour_Movement.Info {
                Type = IDFR_BattleBehaviour_Movement.TYPE,
            };
        }

        protected override void Execute(IDFR_BattleBehaviour_Movement.Info info) {
            var fromLogicWayPosition = m_LastLogicWayPosition;
            var fromWayPosition = fromLogicWayPosition.Float;
            var startWayPosition = fromWayPosition;
            var endWayPosition = Entity.LogicWayPosition.Float;
            m_LastLogicWayPosition = Entity.LogicWayPosition;
            var way = BattleServiceTD.Instance.GetWay(m_Model.WayID);
            endWayPosition = Mathf.Min(endWayPosition, way.Length.Float);
            var distance = endWayPosition - fromWayPosition;
            if (distance <= 0) {
                return;
            }

            var totalDistance = distance;
            while (KHBD.Context.IsApplicationPlaying && info.WayPointQueue != null) {
                var startWayPositionInt = (int)fromWayPosition;
                var nextWayPositionInt = startWayPositionInt + 1;
                var startPoint = way.Get(way.Count - (startWayPositionInt + 1), m_IsReverse);
                var nextAxis = way.GetNextAxis(fromLogicWayPosition, m_IsReverse);
                var sectionInterpolation = fromWayPosition - startWayPositionInt;
                var lerp = Vector2.Lerp(startPoint.Axis, nextAxis, sectionInterpolation);
                var from = BattleServiceTD.Instance.GetLocalPosition(lerp);
                from.z = Transform.localPosition.z;
                var direction = DFR_BattleUtilitiesTD.GetDirection(startPoint.Axis, nextAxis);
                var sectionDistance = nextWayPositionInt - fromWayPosition;
                //UnityEngine.Debug.Log($"{nameof(sectionDistance)}:{sectionDistance}, {nameof(distance)}:{distance}");
                if (sectionDistance >= distance) {
                    var to = DFR_BattleUtilitiesTD.Forward(from, direction, distance);
                    var wayPoint = m_WayPointPool.Get();
                    wayPoint.Initialize(direction, from, to, fromWayPosition, endWayPosition, (fromWayPosition - startWayPosition) / totalDistance, 1f);
                    info.WayPointQueue.Enqueue(wayPoint);
                    break;
                } else {
                    var to = DFR_BattleUtilitiesTD.Forward(from, direction, sectionDistance);
                    var wayPoint = m_WayPointPool.Get();
                    wayPoint.Initialize(direction, from, to, fromWayPosition, nextWayPositionInt, (fromWayPosition - startWayPosition) / totalDistance, (nextWayPositionInt - startWayPosition) / totalDistance);
                    info.WayPointQueue.Enqueue(wayPoint);
                    fromWayPosition = nextWayPositionInt;
                    fromLogicWayPosition = F64.From(fromWayPosition);
                    distance = endWayPosition - fromWayPosition;
                }
            }
        }

        protected override void Update(IDFR_BattleBehaviour_Movement.Info info, float interpolation) {
            StateMachine.State = DFR_BattleEntityState.Move;

            while (info.WayPointQueue != null && info.WayPointQueue.Count > 0 && KHBD.Context.IsApplicationPlaying) {
                var wayPoint = info.WayPointQueue.Peek();
                if (wayPoint.Direction == DFR_BattleDirection.Right && Transform.localScale.x != -1f) {
                    Transform.localScale = new Vector3(-1f, 1f, 1f);
                } else if (wayPoint.Direction == DFR_BattleDirection.Left && Transform.localScale.x != 1f) {
                    Transform.localScale = new Vector3(1f, 1f, 1f);
                }
                var offset = BattleServiceTD.Instance.GetRenderOffsetVector(m_Model.Offset);
                //UnityEngine.Debug.Log($"interpolation:{interpolation}, from:{wayPoint.From} to:{wayPoint.To}, fi:{wayPoint.FromInterpolation}, ti:{wayPoint.ToInterpolation}");
                if (interpolation >= wayPoint.ToInterpolation) {
                    if (info.WayPointQueue.Count == 1) {
                        Entity.WayPosition = wayPoint.ToWayPosition;
                        Transform.localPosition = wayPoint.To + offset;
                    }
                    info.WayPointQueue.Dequeue();
                    m_WayPointPool.Release(wayPoint);
                } else {
                    var wayPointInterpolation = (interpolation - wayPoint.FromInterpolation) / (wayPoint.ToInterpolation - wayPoint.FromInterpolation);
                    var nowWayPosition = Mathf.Lerp(wayPoint.FromWayPosition, wayPoint.ToWayPosition, wayPointInterpolation);
                    var now = Vector3.Lerp(wayPoint.From, wayPoint.To, wayPointInterpolation);
                    Entity.WayPosition = nowWayPosition;
                    Transform.localPosition = now + offset;
                    break;
                }
            }
        }
    }
}
